#pragma once

#include <string>
#include "ProcTable.h"
#include "Utilities.h"

ProcTable::ProcTable(void){
	nameTable = NameTableType();
	indexTable = IndexTableType();
	startEndTable = StartEndTableType();
	index=0;
}

ProcTable::~ProcTable(void){
	clear();
}

void ProcTable::clear(){
	nameTable.clear();
	indexTable.clear();
	startEndTable.clear();
	index = 0;
}

Type::Index::Proc ProcTable::insertProc(Type::Name::Proc procName){
	// invalid procedure name length
	if(!Utilities::isValidProcName(procName)){
		return -1;
	}else if(isProcExistsInProcTable(procName)){
		return getProcIndex(procName);
	}else{
		index++;
		nameTable.insert(std::make_pair<Type::Index::Proc, Type::Name::Proc>(index,procName));
		indexTable.insert(std::make_pair<Type::Name::Proc, Type::Index::Proc >(procName, index));
		return index;
	}
}

bool ProcTable::insertProcStartEnd(Type::Index::Proc procIndex, Type::Index::Line start, Type::Index::Line end){

	if(!Utilities::isValidProcIndex(procIndex) || !Utilities::isValidStartEnd(start, end) 
		|| !isProcExistsInProcTable(procIndex)){
		return false;
	}

	Type::Pair::StartEnd startEndPair;
	bool isNotExisted = true;

	if(startEndTable.find(procIndex) != startEndTable.end()){
		isNotExisted = false;
	}

	if(isNotExisted){
		startEndPair.first = start;
		startEndPair.second = end;
		startEndTable.insert(std::make_pair<Type::Index::Proc, Type::Pair::StartEnd>(procIndex, startEndPair));
	
		// update line-procedure table
		for(int k=start; k<=end;k++){
			lineProcTable.insert(std::make_pair<Type::Index::Line, Type::Index::Proc>(k, procIndex));
		}
	}

	return isNotExisted;
}

const Type::List::Proc& ProcTable::getProcIndices(){
	Type::List::Proc* procList = new Type::List::Proc();
	for(NameTableType::iterator it=nameTable.begin(); it!=nameTable.end(); it++){
		procList->push_back(it -> first);
	}
	return *procList;
}

Type::Index::Proc ProcTable::getProcIndex(Type::Name::Proc procName){
	// valid procedure name length and procedure exists
	if(Utilities::isValidProcName(procName) && isProcExistsInProcTable(procName)){
		return indexTable.at(procName);
	}else{
		return -1;
	}
}

Type::Name::Proc ProcTable::getProcName(Type::Index::Proc  procIndex){
	// valid procedure index and procedure exists
	if(Utilities::isValidProcIndex(procIndex) && isProcExistsInProcTable(procIndex)){
		return nameTable.at(procIndex);
	}else{
		return "";
	}
}

Type::Pair::StartEnd ProcTable::getProcStartEnd(Type::Index::Proc proc){
	Type::Pair::StartEnd startEndPair;

	if(isProcExistsInStartEnd(proc)){
		startEndPair = startEndTable.at(proc);
	}

	return startEndPair;
}

Type::Index::Proc ProcTable::getProcIndexOfLine(Type::Index::Line line){
	if(!Utilities::isValidLineIndex(line) 
		&& lineProcTable.find(line) != lineProcTable.end()){
		return -1;
	}

	return lineProcTable.at(line);
}

bool ProcTable::isProcExistsInProcTable(Type::Name::Proc procName){
	// valid procedure name length and procedure can be found
	if (Utilities::isValidProcName(procName) && indexTable.find(procName) != indexTable.end()){
		return true;
	}else{
		return false;
	}
}

bool ProcTable::isProcExistsInProcTable(Type::Index::Proc  procIndex){
	// valid procedure index and procedure can be found
	if (Utilities::isValidProcIndex(procIndex) && nameTable.find(procIndex) != nameTable.end()){
		return true;
	}else{
		return false;
	}
}

bool ProcTable::isProcExistsInStartEnd(Type::Index::Proc  procIndex){
	// valid procedure index and procedure can be found
	if (Utilities::isValidProcIndex(procIndex) && startEndTable.find(procIndex) != startEndTable.end()){
		return true;
	}else{
		return false;
	}
}

bool ProcTable::setProcExitPoint(Type::Index::Proc proc, Type::Index::Line point){
	
	if (!Utilities::isValidProcIndex(proc)){
		return false;
	}else{
		if (procExitTable.find(proc) != procExitTable.end()){
			procExitTable.find(proc)->second.push_back(point);
		}else{
			Type::List::Line points;
			points.push_back(point);
			procExitTable[proc] = points;
		}
		return true;
	}
}

const Type::List::Line& ProcTable::getProcExitPoints(Type::Index::Proc proc){
	Type::List::Line* points = new Type::List::Proc();
	if (!Utilities::isValidProcIndex(proc)){
		return *points;
	}else{
		if (procExitTable.find(proc) == procExitTable.end()){
			return *points;
		}else{
			return procExitTable.find(proc)->second;
		}
	}
}